Skip to content
This repository was archived by the owner on Mar 9, 2026. It is now read-only.

feat!: v0.2.0 — reshape ParseIssue and strengthen ParseResult types#59

Merged
roottool merged 9 commits intomainfrom
feat/v0.2.0
Mar 1, 2026
Merged

feat!: v0.2.0 — reshape ParseIssue and strengthen ParseResult types#59
roottool merged 9 commits intomainfrom
feat/v0.2.0

Conversation

@roottool
Copy link
Copy Markdown
Owner

@roottool roottool commented Mar 1, 2026

Description

Implements all breaking changes planned for v0.2.0, focused on making ParseIssue and ParseResult more precise and honest.

  • Remove ParseIssue.path (was always readonly [] — carried no information)
  • Add ParseIssue.key: string (required, identifies the offending FormData key)
  • Narrow ParseResult failure issues to a non-empty tuple [ParseIssue, ...ParseIssue[]]
  • Export SuccessResult and FailureResult named utility types
  • Update all documentation, examples, and skill references to match

Type of Change

  • Refactoring (non-breaking change improving code structure)
  • Bugfix (non-breaking change fixing an issue)
  • Security fix (non-breaking change addressing a security issue)
  • Documentation (changes to documentation only)
  • Tests (adding or updating tests)
  • Tooling / CI (changes to build tools, CI configuration)

Note: Despite the "refactoring" checkbox, this PR contains breaking changes to the public API under the v0.x versioning policy (minor bump = effective major).

Boundary Checklist (Required for Implementation Changes)

  • No interpretation
    • Key names are treated as opaque strings
    • No structural inference ([], ., brackets, paths, etc.)
  • No silent behavior
    • No merging, overwriting, autofixing, or implicit resolution
    • All boundary violations are reported explicitly
  • Boundary respected
    • No validation, coercion, schema, framework conventions, or business logic

📘 Boundary rules & non-goals:
https://github.com/roottool/safe-formdata/blob/main/AGENTS.md

Security & API Stability

Security Impact

  • No impact on security (no changes to forbidden keys, prototype safety)
  • Reviewed against security rules in AGENTS.md

API Contract

  • No changes to public API (parse function signature, type definitions)
  • If API change: Is this a breaking change? Yes — see migration guide below and CHANGELOG.md

Breaking changes:

  1. ParseIssue.path removed — callers referencing issue.path must delete those references
  2. ParseIssue.key: string added (required) — type definitions referencing ParseIssue must be updated
  3. issues: [ParseIssue, ...ParseIssue[]] on failure — explicit type annotations may need updating

Versioning

  • Change is compatible with current v0.x versioning policy
  • If breaking change proposed: Justification for major version bump
    • Under the 0.x policy, this minor bump is treated as an effective major. Breaking changes are documented in CHANGELOG.md.

Testing

Automated Checks

  • TypeScript type checking passes (bun run check:type:source)
  • All tests pass (bun run test)
  • Build succeeds (bun run build)

Additional Verification

  • Existing tests updated to assert issue.key (removed issue.path assertions)
  • boundary-validator skill run against all changes; findings resolved

Automated Review (Optional)

boundary-validator was run as part of this PR. All findings were resolved:

  • Added explicit ParseIssue[] type annotation to issues array in parse.ts
  • Added comment explaining the destructuring pattern used to avoid type assertion
  • Fixed JSDoc example: if (result.data)if (result.data !== null)
  • Fixed invalid_key description: "unsafe characters" → "empty or not a string"

🤖 Generated with Claude Code

Co-Authored-By: Claude noreply@anthropic.com

roottool and others added 9 commits March 2, 2026 01:28
BREAKING CHANGE: ParseIssue.path (always-empty array for external format
compatibility) is removed. It conveyed no information and implied false
Standard Schema compliance.

BREAKING CHANGE: ParseIssue.key type changes from optional `unknown` to
required `string`. FormData keys are always strings per spec; the previous
type was misleading.

ParseIssue is now: { code: IssueCode; key: string }

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Reflect removal of path field and key type change (unknown → string)
in AGENTS.md, README.md, and all boundary-validator skill references.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
BREAKING CHANGE: ParseResult failure branch issues type changes from
ParseIssue[] to [ParseIssue, ...ParseIssue[]], reflecting the invariant
that at least one issue always exists when data is null.

Uses destructuring at the return site to let TypeScript infer the
non-empty tuple type without a type assertion.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Derived from ParseResult via Extract, providing named aliases for
each branch of the discriminated union without changing ParseResult itself.

  SuccessResult = Extract<ParseResult, { data: Record<string, string | File> }>
  FailureResult = Extract<ParseResult, { data: null }>

FailureResult.issues is [ParseIssue, ...ParseIssue[]], reflecting the
non-empty tuple introduced in the previous commit.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The previous description claimed keys with characters outside [a-zA-Z0-9_-]
were rejected, but the implementation only rejects empty strings and
non-string keys. Keys are treated as opaque strings per design.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Object.create(null) returns any; using a type annotation instead of
a cast (as) lets TypeScript verify assignability rather than silence it.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Add explicit `ParseIssue[]` type annotation to `issues` array (was inferred as `never[]`)
- Add comment explaining destructuring pattern used to avoid type assertion
- Fix JSDoc example: `if (result.data)` → `if (result.data !== null)`
- Fix JSDoc description: "unsafe characters" → "empty or not a string" for invalid_key

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ParseResult type in api-contract

- Add SuccessResult/FailureResult utility types to Public API section
- Update ParseResult failure branch: ParseIssue[] → [ParseIssue, ...ParseIssue[]]
- Update constraint description to mention "non-empty tuple"
- Add checklist item for SuccessResult/FailureResult derivation rule
- Update Last updated date

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Document breaking changes with before/after examples and migration steps:
- ParseIssue.path removal
- ParseIssue.key addition (required string)
- issues type narrowed to non-empty tuple [ParseIssue, ...ParseIssue[]]

Use [Unreleased] header to align with planned workflow automation
that will auto-rewrite it to [v{version}] at release time.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@roottool roottool self-assigned this Mar 1, 2026
@roottool roottool merged commit dcdd746 into main Mar 1, 2026
11 checks passed
@roottool roottool deleted the feat/v0.2.0 branch March 1, 2026 17:25
@codecov
Copy link
Copy Markdown

codecov Bot commented Mar 1, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 100.00%. Comparing base (07dffcc) to head (4a201ca).
⚠️ Report is 3 commits behind head on main.

Additional details and impacted files
@@            Coverage Diff            @@
##              main       #59   +/-   ##
=========================================
  Coverage   100.00%   100.00%           
=========================================
  Files            3         3           
  Lines           22        23    +1     
  Branches         6         5    -1     
=========================================
+ Hits            22        23    +1     
🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@roottool roottool added the breaking change Introduces a backward-incompatible API or behavior change label Mar 1, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

breaking change Introduces a backward-incompatible API or behavior change

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant